/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.orogeny.actions;

import static com.softwarecraftsmen.archivers.zip.ZipArchiver.ZipArchive;
import com.softwarecraftsmen.archivers.zip.zipEntryAndData.AbsoluteFileBackedZipEntryAndData;
import com.softwarecraftsmen.archivers.zip.zipEntryAndData.ZipEntryAndData;
import static com.softwarecraftsmen.collections.CompositeIterator.compositeIterator;
import com.softwarecraftsmen.orogeny.actions.zip.InputZipIterator;
import static com.softwarecraftsmen.orogeny.actions.ManifestAction.manifest;
import com.softwarecraftsmen.orogeny.buildEnvironments.BuildEnvironment;
import com.softwarecraftsmen.orogeny.buildLogs.BuildLog;
import static com.softwarecraftsmen.orogeny.buildLogs.Verbosity.Summary;
import com.softwarecraftsmen.orogeny.execution.SuccessOrFailure;
import static com.softwarecraftsmen.orogeny.execution.SuccessOrFailure.Failure;
import static com.softwarecraftsmen.orogeny.execution.SuccessOrFailure.Success;
import static com.softwarecraftsmen.orogeny.filing.AbsoluteDirectoriesAndFiles.absoluteDirectoriesAndFiles;
import com.softwarecraftsmen.orogeny.filing.AbsoluteFile;
import static com.softwarecraftsmen.orogeny.filing.AbsoluteFile.createTemporaryFile;
import com.softwarecraftsmen.orogeny.filing.AbsolutePath;
import com.softwarecraftsmen.orogeny.filing.AbsolutePaths;
import com.softwarecraftsmen.orogeny.filing.FileName;
import static com.softwarecraftsmen.orogeny.filing.Folder.folder;
import static com.softwarecraftsmen.orogeny.filing.RelativeDirectory.relativeDirectory;
import com.softwarecraftsmen.orogeny.filing.findFileFilters.FindFilesFilter;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import static java.lang.String.format;
import java.util.LinkedHashSet;
import static java.util.Locale.UK;

public class JarTogetherAction extends AbstractAction
{
	private final AbsolutePaths zipPath;
	private final FindFilesFilter zipAndJarFiles;
	private final AbsoluteFile zipFile;
	private final AbsolutePaths classPath;
	private final String mainClassName;

	public JarTogetherAction(final @NotNull AbsolutePaths zipPath, final @NotNull FindFilesFilter zipAndJarFiles, final @NotNull AbsoluteFile zipFile, final @NotNull AbsolutePaths classPath, final @NotNull String mainClassName)
	{
		this.zipPath = zipPath;
		this.zipAndJarFiles = zipAndJarFiles;
		this.zipFile = zipFile;
		this.classPath = classPath;
		this.mainClassName = mainClassName;
	}

	@NotNull
	public String describeWhatWeDo()
	{
		return "jarTogether";
	}

	@NotNull
	private static final String NoMainClassName = "";

	@NotNull
	@SuppressWarnings("unchecked")
	public SuccessOrFailure execute(final @NotNull BuildLog buildLog, final @NotNull BuildEnvironment buildEnvironment)
	{
		buildLog.writeMessage(Summary, format(UK, "About to jarTogether from %1$s files %2$s to zip %3$s with class path %4$s and main class name %5$s", zipPath, zipAndJarFiles, zipFile, classPath, mainClassName));

		final AbsoluteFile manifestFile;
		try
		{
			manifestFile = createTemporaryFile("MANIFEST.MF");
		}
		catch (IOException e)
		{
			return Failure(e);
		}
		manifest(manifestFile).withClassPath(classPath).withMainClass(mainClassName).execute(buildLog, buildEnvironment);
		try
		{
			ZipArchive.archive
			(
				zipFile,
				compositeIterator
				(
					new LinkedHashSet<ZipEntryAndData>()
					{{
						add(new AbsoluteFileBackedZipEntryAndData(manifestFile, relativeDirectory(folder("META-INF")).file(new FileName("MANIFEST.MF"))));
					}}.iterator(),
					new InputZipIterator
					(
						buildLog,
						zipPath.findFiles(zipAndJarFiles)
					)
				)
			);
		}
		catch (Exception e)
		{
			return Failure(e);
		}
		return Success;
	}

	private boolean noManifest() {return !mainClassName.equals(NoMainClassName);}

	@NotNull
	public static SpecifyZipPath jarTogether(final @NotNull AbsolutePath... zipPath)
	{
		return jarTogether(absoluteDirectoriesAndFiles(zipPath));
	}

	@NotNull
	public static SpecifyZipPath jarTogether(final @NotNull AbsolutePaths zipPath)
	{
		return new SpecifyZipPath(zipPath);
	}

	public static final class SpecifyZipPath
	{
		private final AbsolutePaths zipPath;
		private static final AbsolutePath[] EmptyClassPath = new AbsolutePath[] {};

		private SpecifyZipPath(final @NotNull AbsolutePaths zipPath) {this.zipPath = zipPath;}

		public SpecifyZipAndJarFiles capturing(final FindFilesFilter zipAndJarFiles)
		{
			return new SpecifyZipAndJarFiles(zipAndJarFiles);
		}

		public final class SpecifyZipAndJarFiles
		{
			private final FindFilesFilter zipAndJarFiles;

			private SpecifyZipAndJarFiles(final @NotNull FindFilesFilter zipAndJarFiles) {this.zipAndJarFiles = zipAndJarFiles;}

			public SpecifyZipFile to(final @NotNull AbsoluteFile zipFile)
			{
				return new SpecifyZipFile(zipFile);
			}

			public final class SpecifyZipFile
			{
				private final AbsoluteFile zipFile;

				private SpecifyZipFile(final @NotNull AbsoluteFile zipFile) {this.zipFile = zipFile;}

				@NotNull
				public SpecifyClassPath withClassPath(final @NotNull AbsolutePath... classPath)
				{
					return withClassPath(absoluteDirectoriesAndFiles(classPath));
				}

				@NotNull
				public SpecifyClassPath withClassPath(final @NotNull AbsolutePaths... classPath)
				{
					return new SpecifyClassPath(absoluteDirectoriesAndFiles(classPath));
				}

				@NotNull
				public SpecifyClassPath withoutClassPath()
				{
					return withClassPath(EmptyClassPath);
				}

				public final class SpecifyClassPath extends AbstractAction
				{
					private final AbsolutePaths classPath;

					private SpecifyClassPath(final @NotNull AbsolutePaths classPath)
					{
						this.classPath = classPath;
					}

					@NotNull
					public String describeWhatWeDo()
					{
						return new JarTogetherAction(zipPath, zipAndJarFiles, zipFile, classPath, NoMainClassName).describeWhatWeDo();
					}

					@NotNull
					public SuccessOrFailure execute(final @NotNull BuildLog buildLog, final @NotNull BuildEnvironment buildEnvironment)
					{
						return new JarTogetherAction(zipPath, zipAndJarFiles, zipFile, classPath, NoMainClassName).execute(buildLog, buildEnvironment);
					}

					@NotNull
					public JarTogetherAction withMainClass(final @NotNull String mainClassName)
					{
						return new JarTogetherAction(zipPath, zipAndJarFiles, zipFile, classPath, mainClassName);
					}

					@NotNull
					public JarTogetherAction withoutMainClass()
					{
						return new JarTogetherAction(zipPath, zipAndJarFiles, zipFile, classPath, NoMainClassName);
					}
				}
			}
		}
	}
}
